package com.bourne.service.impl;

import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.bourne.constant.NumConstant;
import com.bourne.constant.StringConstant;
import com.bourne.enums.BizCodeEnum;
import com.bourne.enums.RoleEnum;
import com.bourne.enums.StringEnum;
import com.bourne.mapper.UserMapper;
import com.bourne.model.LoginUser;
import com.bourne.model.UserDO;
import com.bourne.model.UserDetailsDO;
import com.bourne.request.UserLoginRequest;
import com.bourne.request.UserRegisterRequest;
import com.bourne.service.UserDetailsService;
import com.bourne.service.UserService;
import com.bourne.util.*;
import com.bourne.vo.UserVO;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.codec.digest.Md5Crypt;
import org.apache.commons.collections.CollectionUtils;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cloud.context.config.annotation.RefreshScope;
import org.springframework.context.annotation.Primary;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.data.redis.core.script.DefaultRedisScript;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import javax.servlet.http.HttpServletRequest;
import java.security.NoSuchAlgorithmException;
import java.time.Duration;
import java.util.*;
import java.util.concurrent.TimeUnit;

/**
 * @program UserServiceImpl
 * @description https://gitee.com/denny_521/frame.git
 * @author: bourne
 * @create 2021-12-09 20:12:34
 **/
@Service
@Slf4j
@Primary
@RefreshScope
public class UserServiceImpl extends BaseCoreServiceImpl<UserMapper, UserDO> implements UserService {

    @Autowired
    private UserDetailsService userDetailsService;

    @Autowired
    private StringRedisTemplate redisTemplate;

    @Autowired(required = false)
    private PageUtils<UserDO> pageUtils;

    /**
     * @param registerRequest 用户注册对象
     * @return JsonData
     * @GlobalTransactional 可使用分布式事务Seata
     */
    @Override
    @Transactional
    public JsonData register(UserRegisterRequest registerRequest) {

        boolean checkCode = false;
        String code = registerRequest.getCode();
        if (StringUtils.isBlank(code)) {
            return JsonData.buildResult(BizCodeEnum.CODE_ERROR);
        }
//        if (StringUtils.isNotBlank(registerRequest.getPhone())){
//            checkCode = notifyService.checkCode(SendCodeEnum.USER_REGISTER, registerRequest.getMail(), code);
//        }
//        if (!checkCode){
//            return JsonData.buildResult(BizCodeEnum.CODE_ERROR);
//        }

        if (!StringUtils.equals(registerRequest.getPwd(), registerRequest.getRePwd())) {
            log.error("两次输入的密码不正确");
            return JsonData.buildResult(BizCodeEnum.ACCOUNT_RE_PASS_WORD_ERROR);
        }

        UserDO user = new UserDO();
        BeanUtils.copyProperties(registerRequest, user);
        user.setPoints(NumConstant.ZERO);
        user.setSecret(StringConstant.SALT_PRE_SECRET + CommonUtil.getNumberRandom(NumConstant.SECRET_NUM));
        String cryptPwd = Md5Crypt.md5Crypt(registerRequest.getPwd().getBytes(), user.getSecret());
        user.setPwd(cryptPwd);
        user.setIdentities(RoleEnum.ORDINARY.getCode());
        try {
            Map<String, String> rsaMap = RSAUtils.genKeyPair();
            user.setRsaPub(rsaMap.get(StringEnum.PUB_KEY.name()));
            user.setRsaPri(rsaMap.get(StringEnum.PRI_KEY.name()));
        } catch (NoSuchAlgorithmException e) {
            log.error("生成RSA密钥对失败");
            return JsonData.buildResult(BizCodeEnum.ACCOUNT_RSA_ERROR);
        }

        // 账号唯一性验证
        if(checkUnique(user.getPhone())){
            int rows = this.insert(user);
            log.info("rows：{}注册成功：{}", rows, user);

            UserDetailsDO details = new UserDetailsDO(user);
            userDetailsService.insert(details);

            return JsonData.buildSuccess();
        } else {
            return JsonData.buildResult(BizCodeEnum.ACCOUNT_REPEAT);
        }
    }


    /**
     * 用户账号唯一性 （数据库设置手机号唯一：唯一索引）
     * @param phone
     * @return
     */
    private boolean checkUnique(String phone){
        QueryWrapper<UserDO> queryWrapper = new QueryWrapper<UserDO>().eq("phone", phone);
        List<UserDO> list = mapper.selectList(queryWrapper);
        return list.size() <= NumConstant.ZERO;
    }

    /**
     * 用户登录
     * @param loginRequest
     * @param request
     * @return
     */
    @Override
    public JsonData login(UserLoginRequest loginRequest, HttpServletRequest request) {

        List<UserDO> userList = mapper.selectList(new QueryWrapper<UserDO>().eq("phone", loginRequest.getPhone()));
        if (CollectionUtils.isEmpty(userList) && userList.size() != NumConstant.ONE) {
            return JsonData.buildResult(BizCodeEnum.ACCOUNT_PWD_ERROR);
        }
        UserDO user = userList.get(NumConstant.ZERO);
        String cryptPwd = Md5Crypt.md5Crypt(loginRequest.getPwd().getBytes(), user.getSecret());
        if (!StringUtils.equals(cryptPwd, user.getPwd())){
            return JsonData.buildResult(BizCodeEnum.ACCOUNT_PWD_ERROR);
        }
        LoginUser loginUser = LoginUser.builder().build();
        BeanUtils.copyProperties(user, loginUser);
        String token = JwtUtil.geneJsonWebToken(loginUser);
        return JsonData.buildSuccess(token);
    }

    /**
     * 查询用户详情 redssion
     * @return
     */
    @Override
    public UserVO findUserDetail(Long id) {
        log.info("查询用户详情id为{}", id);
        UserDO userDO = mapper.selectOne(new LambdaQueryWrapper<UserDO>().select(UserDO::getPhone, UserDO::getName).eq(UserDO::getId, id));
        UserVO userVo = new UserVO();
        BeanUtils.copyProperties(userDO, userVo);
        userVo.setHeadImg("https://wpimg.wallstcn.com/f778738c-e4f8-4870-b634-56703b4acafe.gif");
        return userVo;

//        redisTemplate.opsForValue().setIfAbsent(UUID.randomUUID().toString(), userDO.getPhone(), Duration.ofSeconds(60));
//        UserDO select = new UserDO();
//        select.setPhone("14763477521");
//        List<UserDO> list = this.list(this.getQueryWrapper(select, UserDO.class));

    }

    // 分布式锁
    private void lock(String lockKey, String uuid) {

        String script = "if redis.call('get',KEYS[1]) == ARGV[1] then return redis.call('del',KEYS[1]) else return 0 end";
        boolean ULock = redisTemplate.opsForValue().setIfAbsent(lockKey, uuid, Duration.ofSeconds(30));
        log.info("{}加锁状态：{}", uuid, ULock);
        if (ULock) {
                try {
                    // TODO 处理业务
                    TimeUnit.MILLISECONDS.sleep(10000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                } finally {
                    Long result = redisTemplate.execute(new DefaultRedisScript<>(script, Long.class), Arrays.asList(lockKey), uuid);
                    log.info("{}解锁状态：{}", uuid, result);
                }
        } else {
            try {
                TimeUnit.MILLISECONDS.sleep(2000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            log.info("{}加锁失败，进入自旋", uuid);
            lock(lockKey, uuid);
        }
    }



    @Transactional
    @Override
    public Object testSql(UserDO userDO) {

        // 分页实例
        Page<UserDO> page = pageUtils.pageInfoInit();
        QueryWrapper<UserDO> queryWrapper = this.getQueryWrapper(userDO, UserDO.class, false);
        Page<UserDO> userDOPage = mapper.selectPage(page, queryWrapper);
        Map<String, Object> map = pageUtils.pageDataMap(userDOPage);
        return map;
//        // 分组查询
//        QueryWrapper<UserDO> having = queryWrapper.select("name").groupBy("name").having("count(name)");
//        List<UserDO> userDOS = mapper.selectList(having);

        // 只会返回第一个字段（第一列）的值，其他字段会被舍弃
//        queryWrapper.select("id, name, mail, points");
//        List<Object> objects = mapper.selectObjs(queryWrapper);

        // 统计
//        Integer integer = mapper.selectCount(new QueryWrapper<>());
        // lambdaQuery
//        List<UserDO> eric = this.lambdaQuery().eq(UserDO::getName, "single").select(UserDO::getName, UserDO::getId).list();
//        queryWrapper.apply("date_format(create_time, '%Y-%m-%d') = '2021-10-11'")
//                .inSql("name", "select name where name like 'eri%'");
//        List<UserDO> userDOS = mapper.selectList(queryWrapper);

        // 排除某些字段不反回
//        QueryWrapper<UserDO> select = queryWrapper.select(UserDO.class, info -> {
//            String columnName = info.getColumn();
//            return !"name".equals(columnName) && !"points".equals(columnName);
//        });
//        List<UserDO> userDOS = mapper.selectList(select);

//        String str = "";
//        QueryWrapper<UserDO> queryWrapper1 = queryWrapper.select().eq(StringUtils.isNotBlank(str), "name", "eric");
//        List<UserDO> userDOS = mapper.selectList(queryWrapper1);
//        UserDO userDO = new UserDO();
//        userDO.setName("eric");
//        List<UserDO> userDOS = mapper.selectList(new QueryWrapper<>(userDO));

//        Map<String, Object> param = new HashMap<>();
//        param.put("name", "bourne");
//        queryWrapper.allEq(param);
//        List<UserDO> users = mapper.selectList(queryWrapper);

//        Page<UserDO> page = new Page<>(1, 1);
//        IPage<UserDO> selectPage = mapper.selectPage(page, new QueryWrapper<>());
//        PageData<UserDO> back = new PageData<>(selectPage);
//        int back = mapper.deleteById(3);
//        userDO.setName(userDO.getId() + "update_tran");
//        int back = mapper.updateById(userDO);
//        if (userDO.getId() == 2) {
//            throw new RuntimeException("更新失败");
//        }
//        return back;
    }
}
